/*
* java
* Copyright (C) 2004
* 
* $Id: CL_ents.java,v 1.10 2005/02/06 19:17:03 salomo Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Suake2.UI.game;
using Suake2.UI.qcommon;
using Math3D = Suake2.UI.util.Math3D;
namespace Suake2.UI.client
{
	
	/// <summary> CL_ents</summary>
	//	   cl_ents.c -- entity parsing and management
	/*
	* =========================================================================
	* 
	* FRAME PARSING
	* 
	* =========================================================================
	*/
	public class CL_ents
	{
		
		internal static int[] bitcounts = new int[32]; /// just for protocol profiling
		
		internal static int[] bfg_lightramp = new int[]{300, 400, 600, 300, 150, 75};
		
		/*
		* ================= CL_ParseEntityBits
		* 
		* Returns the entity number and the header bits =================
		*/
		public static int ParseEntityBits(int[] bits)
		{
			int b, total;
			int i;
			int number;
			
			total = MSG.ReadByte(Globals.net_message);
			if ((total & Defines.U_MOREBITS1) != 0)
			{
				
				b = MSG.ReadByte(Globals.net_message);
				total |= b << 8;
			}
			if ((total & Defines.U_MOREBITS2) != 0)
			{
				
				b = MSG.ReadByte(Globals.net_message);
				total |= b << 16;
			}
			if ((total & Defines.U_MOREBITS3) != 0)
			{
				
				b = MSG.ReadByte(Globals.net_message);
				total |= b << 24;
			}
			
			// count the bits for net profiling
			for (i = 0; i < 32; i++)
				if ((total & (1 << i)) != 0)
					bitcounts[i]++;
			
			if ((total & Defines.U_NUMBER16) != 0)
				number = MSG.ReadShort(Globals.net_message);
			else
				number = MSG.ReadByte(Globals.net_message);
			
			bits[0] = total;
			
			return number;
		}
		
		/*
		* ================== CL_ParseDelta
		* 
		* Can go from either a baseline or a previous packet_entity
		* ==================
		*/
		public static void  ParseDelta(entity_state_t from, entity_state_t to, int number, int bits)
		{
			// set everything to the state we are delta'ing from
			to.set_Renamed(from);
			
			Math3D.VectorCopy(from.origin, to.old_origin);
			to.number = number;
			
			if ((bits & Defines.U_MODEL) != 0)
				to.modelindex = MSG.ReadByte(Globals.net_message);
			if ((bits & Defines.U_MODEL2) != 0)
				to.modelindex2 = MSG.ReadByte(Globals.net_message);
			if ((bits & Defines.U_MODEL3) != 0)
				to.modelindex3 = MSG.ReadByte(Globals.net_message);
			if ((bits & Defines.U_MODEL4) != 0)
				to.modelindex4 = MSG.ReadByte(Globals.net_message);
			
			if ((bits & Defines.U_FRAME8) != 0)
				to.frame = MSG.ReadByte(Globals.net_message);
			if ((bits & Defines.U_FRAME16) != 0)
				to.frame = MSG.ReadShort(Globals.net_message);
			
			if ((bits & Defines.U_SKIN8) != 0 && (bits & Defines.U_SKIN16) != 0)
			//used
			// for
			// laser
			// colors
				to.skinnum = MSG.ReadLong(Globals.net_message);
			else if ((bits & Defines.U_SKIN8) != 0)
				to.skinnum = MSG.ReadByte(Globals.net_message);
			else if ((bits & Defines.U_SKIN16) != 0)
				to.skinnum = MSG.ReadShort(Globals.net_message);
			
			if ((bits & (Defines.U_EFFECTS8 | Defines.U_EFFECTS16)) == (Defines.U_EFFECTS8 | Defines.U_EFFECTS16))
				to.effects = MSG.ReadLong(Globals.net_message);
			else if ((bits & Defines.U_EFFECTS8) != 0)
				to.effects = MSG.ReadByte(Globals.net_message);
			else if ((bits & Defines.U_EFFECTS16) != 0)
				to.effects = MSG.ReadShort(Globals.net_message);
			
			if ((bits & (Defines.U_RENDERFX8 | Defines.U_RENDERFX16)) == (Defines.U_RENDERFX8 | Defines.U_RENDERFX16))
				to.renderfx = MSG.ReadLong(Globals.net_message);
			else if ((bits & Defines.U_RENDERFX8) != 0)
				to.renderfx = MSG.ReadByte(Globals.net_message);
			else if ((bits & Defines.U_RENDERFX16) != 0)
				to.renderfx = MSG.ReadShort(Globals.net_message);
			
			if ((bits & Defines.U_ORIGIN1) != 0)
				to.origin[0] = MSG.ReadCoord(Globals.net_message);
			if ((bits & Defines.U_ORIGIN2) != 0)
				to.origin[1] = MSG.ReadCoord(Globals.net_message);
			if ((bits & Defines.U_ORIGIN3) != 0)
				to.origin[2] = MSG.ReadCoord(Globals.net_message);
			
			if ((bits & Defines.U_ANGLE1) != 0)
				to.angles[0] = MSG.ReadAngle(Globals.net_message);
			if ((bits & Defines.U_ANGLE2) != 0)
				to.angles[1] = MSG.ReadAngle(Globals.net_message);
			if ((bits & Defines.U_ANGLE3) != 0)
				to.angles[2] = MSG.ReadAngle(Globals.net_message);
			
			if ((bits & Defines.U_OLDORIGIN) != 0)
				MSG.ReadPos(Globals.net_message, to.old_origin);
			
			if ((bits & Defines.U_SOUND) != 0)
				to.sound = MSG.ReadByte(Globals.net_message);
			
			if ((bits & Defines.U_EVENT) != 0)
				to.event_Renamed = MSG.ReadByte(Globals.net_message);
			else
				to.event_Renamed = 0;
			
			if ((bits & Defines.U_SOLID) != 0)
				to.solid = MSG.ReadShort(Globals.net_message);
		}
		
		/*
		* ================== CL_DeltaEntity
		* 
		* Parses deltas from the given base and adds the resulting entity to the
		* current frame ==================
		*/
		public static void  DeltaEntity(frame_t frame, int newnum, entity_state_t old, int bits)
		{
			centity_t ent;
			entity_state_t state;
			
			ent = Globals.cl_entities[newnum];
			
			state = Globals.cl_parse_entities[Globals.cl.parse_entities & (Defines.MAX_PARSE_ENTITIES - 1)];
			Globals.cl.parse_entities++;
			frame.num_entities++;
			
			ParseDelta(old, state, newnum, bits);
			
			// some data changes will force no lerping
			if (state.modelindex != ent.current.modelindex || state.modelindex2 != ent.current.modelindex2 || state.modelindex3 != ent.current.modelindex3 || state.modelindex4 != ent.current.modelindex4 || System.Math.Abs(state.origin[0] - ent.current.origin[0]) > 512 || System.Math.Abs(state.origin[1] - ent.current.origin[1]) > 512 || System.Math.Abs(state.origin[2] - ent.current.origin[2]) > 512 || state.event_Renamed == Defines.EV_PLAYER_TELEPORT || state.event_Renamed == Defines.EV_OTHER_TELEPORT)
			{
				ent.serverframe = - 99;
			}
			
			if (ent.serverframe != Globals.cl.frame.serverframe - 1)
			{
				// wasn't in
				// last
				// update, so
				// initialize
				// some
				// things
				ent.trailcount = 1024; // for diminishing rocket / grenade trails
				// duplicate the current state so lerping doesn't hurt anything
				ent.prev.set_Renamed(state);
				if (state.event_Renamed == Defines.EV_OTHER_TELEPORT)
				{
					Math3D.VectorCopy(state.origin, ent.prev.origin);
					Math3D.VectorCopy(state.origin, ent.lerp_origin);
				}
				else
				{
					Math3D.VectorCopy(state.old_origin, ent.prev.origin);
					Math3D.VectorCopy(state.old_origin, ent.lerp_origin);
				}
			}
			else
			{
				// shuffle the last state to previous
				// Copy !
				ent.prev.set_Renamed(ent.current);
			}
			
			ent.serverframe = Globals.cl.frame.serverframe;
			// Copy !
			ent.current.set_Renamed(state);
		}
		
		// call by reference
		//UPGRADE_NOTE: Final was removed from the declaration of 'iw'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly int[] iw = new int[]{0};
		/*
		* ================== CL_ParsePacketEntities
		* 
		* An svc_packetentities has just been parsed, deal with the rest of the
		* data stream. ==================
		*/
		public static void  ParsePacketEntities(frame_t oldframe, frame_t newframe)
		{
			int newnum;
			int bits = 0;
			
			entity_state_t oldstate = null;
			int oldnum;
			
			newframe.parse_entities = Globals.cl.parse_entities;
			newframe.num_entities = 0;
			
			// delta from the entities present in oldframe
			int oldindex = 0;
			if (oldframe == null)
				oldnum = 99999;
			else
			{
				// oldindex == 0. hoz
				//			if (oldindex >= oldframe.num_entities)
				//				oldnum = 99999;
				//			else {
				oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
				oldnum = oldstate.number;
				//			}
			}
			
			while (true)
			{
				//int iw[] = { bits };
				iw[0] = bits;
				newnum = ParseEntityBits(iw);
				bits = iw[0];
				
				if (newnum >= Defines.MAX_EDICTS)
					Com.Error(Defines.ERR_DROP, "CL_ParsePacketEntities: bad number:" + newnum);
				
				if (Globals.net_message.readcount > Globals.net_message.cursize)
					Com.Error(Defines.ERR_DROP, "CL_ParsePacketEntities: end of message");
				
				if (0 == newnum)
					break;
				
				while (oldnum < newnum)
				{
					// one or more entities from the old
					// packet are unchanged
					if (Globals.cl_shownet.value_Renamed == 3)
						Com.Printf("   unchanged: " + oldnum + "\n");
					DeltaEntity(newframe, oldnum, oldstate, 0);
					
					oldindex++;
					
					if (oldindex >= oldframe.num_entities)
						oldnum = 99999;
					else
					{
						oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
						oldnum = oldstate.number;
					}
				}
				
				if ((bits & Defines.U_REMOVE) != 0)
				{
					// the entity present in
					// oldframe is not in the
					// current frame
					if (Globals.cl_shownet.value_Renamed == 3)
						Com.Printf("   remove: " + newnum + "\n");
					if (oldnum != newnum)
						Com.Printf("U_REMOVE: oldnum != newnum\n");
					
					oldindex++;
					
					if (oldindex >= oldframe.num_entities)
						oldnum = 99999;
					else
					{
						oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
						oldnum = oldstate.number;
					}
					continue;
				}
				
				if (oldnum == newnum)
				{
					// delta from previous state
					if (Globals.cl_shownet.value_Renamed == 3)
						Com.Printf("   delta: " + newnum + "\n");
					DeltaEntity(newframe, newnum, oldstate, bits);
					
					oldindex++;
					
					if (oldindex >= oldframe.num_entities)
						oldnum = 99999;
					else
					{
						oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
						oldnum = oldstate.number;
					}
					continue;
				}
				
				if (oldnum > newnum)
				{
					// delta from baseline
					if (Globals.cl_shownet.value_Renamed == 3)
						Com.Printf("   baseline: " + newnum + "\n");
					DeltaEntity(newframe, newnum, Globals.cl_entities[newnum].baseline, bits);
					continue;
				}
			}
			
			// any remaining entities in the old frame are copied over
			while (oldnum != 99999)
			{
				// one or more entities from the old packet
				// are unchanged
				if (Globals.cl_shownet.value_Renamed == 3)
					Com.Printf("   unchanged: " + oldnum + "\n");
				DeltaEntity(newframe, oldnum, oldstate, 0);
				
				oldindex++;
				
				if (oldindex >= oldframe.num_entities)
					oldnum = 99999;
				else
				{
					oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
					oldnum = oldstate.number;
				}
			}
		}
		
		/*
		* =================== CL_ParsePlayerstate ===================
		*/
		public static void  ParsePlayerstate(frame_t oldframe, frame_t newframe)
		{
			int flags;
			player_state_t state;
			int i;
			int statbits;
			
			state = newframe.playerstate;
			
			// clear to old value before delta parsing
			if (oldframe != null)
				state.set_Renamed(oldframe.playerstate);
			//memset (state, 0, sizeof(*state));
			else
				state.clear();
			
			flags = MSG.ReadShort(Globals.net_message);
			
			//
			// parse the pmove_state_t
			//
			if ((flags & Defines.PS_M_TYPE) != 0)
				state.pmove.pm_type = MSG.ReadByte(Globals.net_message);
			
			if ((flags & Defines.PS_M_ORIGIN) != 0)
			{
				state.pmove.origin[0] = MSG.ReadShort(Globals.net_message);
				state.pmove.origin[1] = MSG.ReadShort(Globals.net_message);
				state.pmove.origin[2] = MSG.ReadShort(Globals.net_message);
			}
			
			if ((flags & Defines.PS_M_VELOCITY) != 0)
			{
				state.pmove.velocity[0] = MSG.ReadShort(Globals.net_message);
				state.pmove.velocity[1] = MSG.ReadShort(Globals.net_message);
				state.pmove.velocity[2] = MSG.ReadShort(Globals.net_message);
			}
			
			if ((flags & Defines.PS_M_TIME) != 0)
			{
				state.pmove.pm_time = (sbyte) MSG.ReadByte(Globals.net_message);
			}
			
			if ((flags & Defines.PS_M_FLAGS) != 0)
				state.pmove.pm_flags = (sbyte) MSG.ReadByte(Globals.net_message);
			
			if ((flags & Defines.PS_M_GRAVITY) != 0)
				state.pmove.gravity = MSG.ReadShort(Globals.net_message);
			
			if ((flags & Defines.PS_M_DELTA_ANGLES) != 0)
			{
				state.pmove.delta_angles[0] = MSG.ReadShort(Globals.net_message);
				state.pmove.delta_angles[1] = MSG.ReadShort(Globals.net_message);
				state.pmove.delta_angles[2] = MSG.ReadShort(Globals.net_message);
			}
			
			if (Globals.cl.attractloop)
				state.pmove.pm_type = Defines.PM_FREEZE; // demo playback
			
			//
			// parse the rest of the player_state_t
			//
			if ((flags & Defines.PS_VIEWOFFSET) != 0)
			{
				state.viewoffset[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.viewoffset[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.viewoffset[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
			}
			
			if ((flags & Defines.PS_VIEWANGLES) != 0)
			{
				state.viewangles[0] = MSG.ReadAngle16(Globals.net_message);
				state.viewangles[1] = MSG.ReadAngle16(Globals.net_message);
				state.viewangles[2] = MSG.ReadAngle16(Globals.net_message);
			}
			
			if ((flags & Defines.PS_KICKANGLES) != 0)
			{
				
				state.kick_angles[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.kick_angles[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.kick_angles[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
			}
			
			if ((flags & Defines.PS_WEAPONINDEX) != 0)
			{
				state.gunindex = MSG.ReadByte(Globals.net_message);
			}
			
			if ((flags & Defines.PS_WEAPONFRAME) != 0)
			{
				state.gunframe = MSG.ReadByte(Globals.net_message);
				state.gunoffset[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.gunoffset[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.gunoffset[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.gunangles[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.gunangles[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
				state.gunangles[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
			}
			
			if ((flags & Defines.PS_BLEND) != 0)
			{
				state.blend[0] = MSG.ReadByte(Globals.net_message) / 255.0f;
				state.blend[1] = MSG.ReadByte(Globals.net_message) / 255.0f;
				state.blend[2] = MSG.ReadByte(Globals.net_message) / 255.0f;
				state.blend[3] = MSG.ReadByte(Globals.net_message) / 255.0f;
			}
			
			if ((flags & Defines.PS_FOV) != 0)
				state.fov = MSG.ReadByte(Globals.net_message);
			
			if ((flags & Defines.PS_RDFLAGS) != 0)
				state.rdflags = MSG.ReadByte(Globals.net_message);
			
			// parse stats
			statbits = MSG.ReadLong(Globals.net_message);
			for (i = 0; i < Defines.MAX_STATS; i++)
				if ((statbits & (1 << i)) != 0)
					state.stats[i] = MSG.ReadShort(Globals.net_message);
		}
		
		/*
		* ================== CL_FireEntityEvents
		* 
		* ==================
		*/
		public static void  FireEntityEvents(frame_t frame)
		{
			entity_state_t s1;
			int pnum, num;
			
			for (pnum = 0; pnum < frame.num_entities; pnum++)
			{
				num = (frame.parse_entities + pnum) & (Defines.MAX_PARSE_ENTITIES - 1);
				s1 = Globals.cl_parse_entities[num];
				if (s1.event_Renamed != 0)
					CL_fx.EntityEvent(s1);
				
				// EF_TELEPORTER acts like an event, but is not cleared each frame
				if ((s1.effects & Defines.EF_TELEPORTER) != 0)
					CL_fx.TeleporterParticles(s1);
			}
		}
		
		/*
		* ================ CL_ParseFrame ================
		*/
		public static void  ParseFrame()
		{
			int cmd;
			int len;
			frame_t old;
			
			//memset( cl.frame, 0, sizeof(cl.frame));
			Globals.cl.frame.reset();
			
			Globals.cl.frame.serverframe = MSG.ReadLong(Globals.net_message);
			Globals.cl.frame.deltaframe = MSG.ReadLong(Globals.net_message);
			Globals.cl.frame.servertime = Globals.cl.frame.serverframe * 100;
			
			// BIG HACK to let old demos continue to work
			if (Globals.cls.serverProtocol != 26)
				Globals.cl.surpressCount = MSG.ReadByte(Globals.net_message);
			
			if (Globals.cl_shownet.value_Renamed == 3)
				Com.Printf("   frame:" + Globals.cl.frame.serverframe + "  delta:" + Globals.cl.frame.deltaframe + "\n");
			
			// If the frame is delta compressed from data that we
			// no longer have available, we must suck up the rest of
			// the frame, but not use it, then ask for a non-compressed
			// message
			if (Globals.cl.frame.deltaframe <= 0)
			{
				Globals.cl.frame.valid = true; // uncompressed frame
				old = null;
				Globals.cls.demowaiting = false; // we can start recording now
			}
			else
			{
				old = Globals.cl.frames[Globals.cl.frame.deltaframe & Defines.UPDATE_MASK];
				if (!old.valid)
				{
					// should never happen
					Com.Printf("Delta from invalid frame (not supposed to happen!).\n");
				}
				if (old.serverframe != Globals.cl.frame.deltaframe)
				{
					// The frame
					// that the
					// server did
					// the delta
					// from
					// is too old, so we can't reconstruct it properly.
					Com.Printf("Delta frame too old.\n");
				}
				else if (Globals.cl.parse_entities - old.parse_entities > Defines.MAX_PARSE_ENTITIES - 128)
				{
					Com.Printf("Delta parse_entities too old.\n");
				}
				else
					Globals.cl.frame.valid = true; // valid delta parse
			}
			
			// clamp time
			if (Globals.cl.time > Globals.cl.frame.servertime)
				Globals.cl.time = Globals.cl.frame.servertime;
			else if (Globals.cl.time < Globals.cl.frame.servertime - 100)
				Globals.cl.time = Globals.cl.frame.servertime - 100;
			
			// read areabits
			len = MSG.ReadByte(Globals.net_message);
			MSG.ReadData(Globals.net_message, Globals.cl.frame.areabits, len);
			
			// read playerinfo
			cmd = MSG.ReadByte(Globals.net_message);
			CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
			if (cmd != Defines.svc_playerinfo)
				Com.Error(Defines.ERR_DROP, "CL_ParseFrame: not playerinfo");
			ParsePlayerstate(old, Globals.cl.frame);
			
			// read packet entities
			cmd = MSG.ReadByte(Globals.net_message);
			CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
			if (cmd != Defines.svc_packetentities)
				Com.Error(Defines.ERR_DROP, "CL_ParseFrame: not packetentities");
			
			ParsePacketEntities(old, Globals.cl.frame);
			
			// save the frame off in the backup array for later delta comparisons
			Globals.cl.frames[Globals.cl.frame.serverframe & Defines.UPDATE_MASK].set_Renamed(Globals.cl.frame);
			
			if (Globals.cl.frame.valid)
			{
				// getting a valid frame message ends the connection process
				if (Globals.cls.state != Defines.ca_active)
				{
					Globals.cls.state = Defines.ca_active;
					Globals.cl.force_refdef = true;
					
					Globals.cl.predicted_origin[0] = Globals.cl.frame.playerstate.pmove.origin[0] * 0.125f;
					Globals.cl.predicted_origin[1] = Globals.cl.frame.playerstate.pmove.origin[1] * 0.125f;
					Globals.cl.predicted_origin[2] = Globals.cl.frame.playerstate.pmove.origin[2] * 0.125f;
					
					Math3D.VectorCopy(Globals.cl.frame.playerstate.viewangles, Globals.cl.predicted_angles);
					if (Globals.cls.disable_servercount != Globals.cl.servercount && Globals.cl.refresh_prepped)
						SCR.EndLoadingPlaque(); // get rid of loading plaque
				}
				Globals.cl.sound_prepped = true; // can start mixing ambient sounds
				
				// fire entity events
				FireEntityEvents(Globals.cl.frame);
				CL_pred.CheckPredictionError();
			}
		}
		
		/*
		* ==========================================================================
		* 
		* INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
		* 
		* ==========================================================================
		*/
		
		// stack variable
		//UPGRADE_NOTE: Final was removed from the declaration of 'ent '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly entity_t ent = new entity_t();
		/*
		* =============== 
		* CL_AddPacketEntities
		* ===============
		*/
		internal static void  AddPacketEntities(frame_t frame)
		{
			entity_state_t s1;
			float autorotate;
			int i;
			int pnum;
			centity_t cent;
			int autoanim;
			clientinfo_t ci;
			int effects, renderfx;
			
			// bonus items rotate at a fixed rate
			autorotate = Math3D.anglemod(Globals.cl.time / 10);
			
			// brush models can auto animate their frames
			autoanim = 2 * Globals.cl.time / 1000;
			
			//memset( ent, 0, sizeof(ent));
			ent.clear();
			
			for (pnum = 0; pnum < frame.num_entities; pnum++)
			{
				s1 = Globals.cl_parse_entities[(frame.parse_entities + pnum) & (Defines.MAX_PARSE_ENTITIES - 1)];
				
				cent = Globals.cl_entities[s1.number];
				
				effects = s1.effects;
				renderfx = s1.renderfx;
				
				// set frame
				if ((effects & Defines.EF_ANIM01) != 0)
					ent.frame = autoanim & 1;
				else if ((effects & Defines.EF_ANIM23) != 0)
					ent.frame = 2 + (autoanim & 1);
				else if ((effects & Defines.EF_ANIM_ALL) != 0)
					ent.frame = autoanim;
				else if ((effects & Defines.EF_ANIM_ALLFAST) != 0)
					ent.frame = Globals.cl.time / 100;
				else
					ent.frame = s1.frame;
				
				// quad and pent can do different things on client
				if ((effects & Defines.EF_PENT) != 0)
				{
					effects &= ~ Defines.EF_PENT;
					effects |= Defines.EF_COLOR_SHELL;
					renderfx |= Defines.RF_SHELL_RED;
				}
				
				if ((effects & Defines.EF_QUAD) != 0)
				{
					effects &= ~ Defines.EF_QUAD;
					effects |= Defines.EF_COLOR_SHELL;
					renderfx |= Defines.RF_SHELL_BLUE;
				}
				//	  ======
				//	   PMM
				if ((effects & Defines.EF_DOUBLE) != 0)
				{
					effects &= ~ Defines.EF_DOUBLE;
					effects |= Defines.EF_COLOR_SHELL;
					renderfx |= Defines.RF_SHELL_DOUBLE;
				}
				
				if ((effects & Defines.EF_HALF_DAMAGE) != 0)
				{
					effects &= ~ Defines.EF_HALF_DAMAGE;
					effects |= Defines.EF_COLOR_SHELL;
					renderfx |= Defines.RF_SHELL_HALF_DAM;
				}
				//	   pmm
				//	  ======
				ent.oldframe = cent.prev.frame;
				ent.backlerp = 1.0f - Globals.cl.lerpfrac;
				
				if ((renderfx & (Defines.RF_FRAMELERP | Defines.RF_BEAM)) != 0)
				{
					// step origin discretely, because the frames
					// do the animation properly
					Math3D.VectorCopy(cent.current.origin, ent.origin);
					Math3D.VectorCopy(cent.current.old_origin, ent.oldorigin);
				}
				else
				{
					// interpolate origin
					for (i = 0; i < 3; i++)
					{
						ent.origin[i] = ent.oldorigin[i] = cent.prev.origin[i] + Globals.cl.lerpfrac * (cent.current.origin[i] - cent.prev.origin[i]);
					}
				}
				
				// create a new entity
				
				// tweak the color of beams
				if ((renderfx & Defines.RF_BEAM) != 0)
				{
					// the four beam colors are
					// encoded in 32 bits of
					// skinnum (hack)
					ent.alpha = 0.30f;
					ent.skinnum = (s1.skinnum >> ((Globals.rnd.Next(4)) * 8)) & 0xff;
					SupportClass.Random.NextDouble();
					ent.model = null;
				}
				else
				{
					// set skin
					if (s1.modelindex == 255)
					{
						// use custom player skin
						ent.skinnum = 0;
						ci = Globals.cl.clientinfo[s1.skinnum & 0xff];
						ent.skin = ci.skin;
						ent.model = ci.model;
						if (null == ent.skin || null == ent.model)
						{
							ent.skin = Globals.cl.baseclientinfo.skin;
							ent.model = Globals.cl.baseclientinfo.model;
						}
						
						//	  ============
						//	  PGM
						if ((renderfx & Defines.RF_USE_DISGUISE) != 0)
						{
							if (ent.skin.name.StartsWith("players/male"))
							{
								ent.skin = Globals.re.RegisterSkin("players/male/disguise.pcx");
								ent.model = Globals.re.RegisterModel("players/male/tris.md2");
							}
							else if (ent.skin.name.StartsWith("players/female"))
							{
								ent.skin = Globals.re.RegisterSkin("players/female/disguise.pcx");
								ent.model = Globals.re.RegisterModel("players/female/tris.md2");
							}
							else if (ent.skin.name.StartsWith("players/cyborg"))
							{
								ent.skin = Globals.re.RegisterSkin("players/cyborg/disguise.pcx");
								ent.model = Globals.re.RegisterModel("players/cyborg/tris.md2");
							}
						}
						//	  PGM
						//	  ============
					}
					else
					{
						ent.skinnum = s1.skinnum;
						ent.skin = null;
						ent.model = Globals.cl.model_draw[s1.modelindex];
					}
				}
				
				// only used for black hole model right now, FIXME: do better
				if (renderfx == Defines.RF_TRANSLUCENT)
					ent.alpha = 0.70f;
				
				// render effects (fullbright, translucent, etc)
				if ((effects & Defines.EF_COLOR_SHELL) != 0)
					ent.flags = 0;
				// renderfx go on color shell entity
				else
					ent.flags = renderfx;
				
				// calculate angles
				if ((effects & Defines.EF_ROTATE) != 0)
				{
					// some bonus items
					// auto-rotate
					ent.angles[0] = 0;
					ent.angles[1] = autorotate;
					ent.angles[2] = 0;
				}
				// RAFAEL
				else if ((effects & Defines.EF_SPINNINGLIGHTS) != 0)
				{
					ent.angles[0] = 0;
					ent.angles[1] = Math3D.anglemod(Globals.cl.time / 2) + s1.angles[1];
					ent.angles[2] = 180;
					{
						float[] forward = new float[]{0, 0, 0};
						float[] start = new float[]{0, 0, 0};
						
						Math3D.AngleVectors(ent.angles, forward, null, null);
						Math3D.VectorMA(ent.origin, 64, forward, start);
						V.AddLight(start, 100, 1, 0, 0);
					}
				}
				else
				{
					// interpolate angles
					float a1, a2;
					
					for (i = 0; i < 3; i++)
					{
						a1 = cent.current.angles[i];
						a2 = cent.prev.angles[i];
						ent.angles[i] = Math3D.LerpAngle(a2, a1, Globals.cl.lerpfrac);
					}
				}
				
				if (s1.number == Globals.cl.playernum + 1)
				{
					ent.flags |= Defines.RF_VIEWERMODEL; // only draw from mirrors
					// FIXME: still pass to refresh
					
					if ((effects & Defines.EF_FLAG1) != 0)
						V.AddLight(ent.origin, 225, 1.0f, 0.1f, 0.1f);
					else if ((effects & Defines.EF_FLAG2) != 0)
						V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1.0f);
					else if ((effects & Defines.EF_TAGTRAIL) != 0)
					//PGM
						V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f);
					//PGM
					else if ((effects & Defines.EF_TRACKERTRAIL) != 0)
					//PGM
						V.AddLight(ent.origin, 225, - 1.0f, - 1.0f, - 1.0f); //PGM
					
					continue;
				}
				
				// if set to invisible, skip
				if (s1.modelindex == 0)
					continue;
				
				if ((effects & Defines.EF_BFG) != 0)
				{
					ent.flags |= Defines.RF_TRANSLUCENT;
					ent.alpha = 0.30f;
				}
				
				// RAFAEL
				if ((effects & Defines.EF_PLASMA) != 0)
				{
					ent.flags |= Defines.RF_TRANSLUCENT;
					ent.alpha = 0.6f;
				}
				
				if ((effects & Defines.EF_SPHERETRANS) != 0)
				{
					ent.flags |= Defines.RF_TRANSLUCENT;
					// PMM - *sigh* yet more EF overloading
					if ((effects & Defines.EF_TRACKERTRAIL) != 0)
						ent.alpha = 0.6f;
					else
						ent.alpha = 0.3f;
				}
				//	  pmm
				
				// add to refresh list
				V.AddEntity(ent);
				
				// color shells generate a seperate entity for the main model
				if ((effects & Defines.EF_COLOR_SHELL) != 0)
				{
					/*
					* PMM - at this point, all of the shells have been handled if
					* we're in the rogue pack, set up the custom mixing, otherwise
					* just keep going if(Developer_searchpath(2) == 2) { all of the
					* solo colors are fine. we need to catch any of the
					* combinations that look bad (double & half) and turn them into
					* the appropriate color, and make double/quad something special
					*  
					*/
					if ((renderfx & Defines.RF_SHELL_HALF_DAM) != 0)
					{
						if (FS.Developer_searchpath(2) == 2)
						{
							// ditch the half damage shell if any of red, blue, or
							// double are on
							if ((renderfx & (Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE)) != 0)
								renderfx &= ~ Defines.RF_SHELL_HALF_DAM;
						}
					}
					
					if ((renderfx & Defines.RF_SHELL_DOUBLE) != 0)
					{
						if (FS.Developer_searchpath(2) == 2)
						{
							// lose the yellow shell if we have a red, blue, or
							// green shell
							if ((renderfx & (Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_GREEN)) != 0)
								renderfx &= ~ Defines.RF_SHELL_DOUBLE;
							// if we have a red shell, turn it to purple by adding
							// blue
							if ((renderfx & Defines.RF_SHELL_RED) != 0)
								renderfx |= Defines.RF_SHELL_BLUE;
							// if we have a blue shell (and not a red shell), turn
							// it to cyan by adding green
							else if ((renderfx & Defines.RF_SHELL_BLUE) != 0)
							// go to green if it's on already, otherwise do cyan
							// (flash green)
								if ((renderfx & Defines.RF_SHELL_GREEN) != 0)
									renderfx &= ~ Defines.RF_SHELL_BLUE;
								else
									renderfx |= Defines.RF_SHELL_GREEN;
						}
					}
					//				}
					// pmm
					ent.flags = renderfx | Defines.RF_TRANSLUCENT;
					ent.alpha = 0.30f;
					V.AddEntity(ent);
				}
				
				ent.skin = null; // never use a custom skin on others
				ent.skinnum = 0;
				ent.flags = 0;
				ent.alpha = 0;
				
				// duplicate for linked models
				if (s1.modelindex2 != 0)
				{
					if (s1.modelindex2 == 255)
					{
						// custom weapon
						ci = Globals.cl.clientinfo[s1.skinnum & 0xff];
						i = (s1.skinnum >> 8); // 0 is default weapon model
						if (0 == Globals.cl_vwep.value_Renamed || i > Defines.MAX_CLIENTWEAPONMODELS - 1)
							i = 0;
						ent.model = ci.weaponmodel[i];
						if (null == ent.model)
						{
							if (i != 0)
								ent.model = ci.weaponmodel[0];
							if (null == ent.model)
								ent.model = Globals.cl.baseclientinfo.weaponmodel[0];
						}
					}
					else
						ent.model = Globals.cl.model_draw[s1.modelindex2];
					
					// PMM - check for the defender sphere shell .. make it
					// translucent
					// replaces the previous version which used the high bit on
					// modelindex2 to determine transparency
					if (Globals.cl.configstrings[Defines.CS_MODELS + (s1.modelindex2)].ToUpper().Equals("models/items/shell/tris.md2".ToUpper()))
					{
						ent.alpha = 0.32f;
						ent.flags = Defines.RF_TRANSLUCENT;
					}
					// pmm
					
					V.AddEntity(ent);
					
					//PGM - make sure these get reset.
					ent.flags = 0;
					ent.alpha = 0;
					//PGM
				}
				if (s1.modelindex3 != 0)
				{
					ent.model = Globals.cl.model_draw[s1.modelindex3];
					V.AddEntity(ent);
				}
				if (s1.modelindex4 != 0)
				{
					ent.model = Globals.cl.model_draw[s1.modelindex4];
					V.AddEntity(ent);
				}
				
				if ((effects & Defines.EF_POWERSCREEN) != 0)
				{
					ent.model = CL_tent.cl_mod_powerscreen;
					ent.oldframe = 0;
					ent.frame = 0;
					ent.flags |= (Defines.RF_TRANSLUCENT | Defines.RF_SHELL_GREEN);
					ent.alpha = 0.30f;
					V.AddEntity(ent);
				}
				
				// add automatic particle trails
				if ((effects & ~ Defines.EF_ROTATE) != 0)
				{
					if ((effects & Defines.EF_ROCKET) != 0)
					{
						CL_fx.RocketTrail(cent.lerp_origin, ent.origin, cent);
						V.AddLight(ent.origin, 200, 1, 1, 0);
					}
					// PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.
					// EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2...
					// Cheese!
					else if ((effects & Defines.EF_BLASTER) != 0)
					{
						//					CL_BlasterTrail (cent.lerp_origin, ent.origin);
						//	  PGM
						if ((effects & Defines.EF_TRACKER) != 0)
						// lame...
						// problematic?
						{
							CL_newfx.BlasterTrail2(cent.lerp_origin, ent.origin);
							V.AddLight(ent.origin, 200, 0, 1, 0);
						}
						else
						{
							CL_fx.BlasterTrail(cent.lerp_origin, ent.origin);
							V.AddLight(ent.origin, 200, 1, 1, 0);
						}
						//	  PGM
					}
					else if ((effects & Defines.EF_HYPERBLASTER) != 0)
					{
						if ((effects & Defines.EF_TRACKER) != 0)
						// PGM overloaded
						// for blaster2.
							V.AddLight(ent.origin, 200, 0, 1, 0);
						// PGM
						// PGM
						else
							V.AddLight(ent.origin, 200, 1, 1, 0);
					}
					else if ((effects & Defines.EF_GIB) != 0)
					{
						CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
					}
					else if ((effects & Defines.EF_GRENADE) != 0)
					{
						CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
					}
					else if ((effects & Defines.EF_FLIES) != 0)
					{
						CL_fx.FlyEffect(cent, ent.origin);
					}
					else if ((effects & Defines.EF_BFG) != 0)
					{
						
						if ((effects & Defines.EF_ANIM_ALLFAST) != 0)
						{
							CL_fx.BfgParticles(ent);
							i = 200;
						}
						else
						{
							i = bfg_lightramp[s1.frame];
						}
						V.AddLight(ent.origin, i, 0, 1, 0);
					}
					// RAFAEL
					else if ((effects & Defines.EF_TRAP) != 0)
					{
						ent.origin[2] += 32;
						CL_fx.TrapParticles(ent);
						i = (Globals.rnd.Next(100)) + 100;
						V.AddLight(ent.origin, i, 1, 0.8f, 0.1f);
					}
					else if ((effects & Defines.EF_FLAG1) != 0)
					{
						CL_fx.FlagTrail(cent.lerp_origin, ent.origin, 242);
						V.AddLight(ent.origin, 225, 1, 0.1f, 0.1f);
					}
					else if ((effects & Defines.EF_FLAG2) != 0)
					{
						CL_fx.FlagTrail(cent.lerp_origin, ent.origin, 115);
						V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1);
					}
					//	  ======
					//	  ROGUE
					else if ((effects & Defines.EF_TAGTRAIL) != 0)
					{
						CL_newfx.TagTrail(cent.lerp_origin, ent.origin, 220);
						V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f);
					}
					else if ((effects & Defines.EF_TRACKERTRAIL) != 0)
					{
						if ((effects & Defines.EF_TRACKER) != 0)
						{
							float intensity;
							
							//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
							intensity = (float) (50 + (500 * (System.Math.Sin(Globals.cl.time / 500.0) + 1.0)));
							// FIXME - check out this effect in rendition
							if (Globals.vidref_val == Defines.VIDREF_GL)
								V.AddLight(ent.origin, intensity, - 1.0f, - 1.0f, - 1.0f);
							else
								V.AddLight(ent.origin, (- 1.0f) * intensity, 1.0f, 1.0f, 1.0f);
						}
						else
						{
							CL_newfx.Tracker_Shell(cent.lerp_origin);
							V.AddLight(ent.origin, 155, - 1.0f, - 1.0f, - 1.0f);
						}
					}
					else if ((effects & Defines.EF_TRACKER) != 0)
					{
						CL_newfx.TrackerTrail(cent.lerp_origin, ent.origin, 0);
						// FIXME - check out this effect in rendition
						if (Globals.vidref_val == Defines.VIDREF_GL)
							V.AddLight(ent.origin, 200, - 1, - 1, - 1);
						else
							V.AddLight(ent.origin, - 200, 1, 1, 1);
					}
					//	  ROGUE
					//	  ======
					// RAFAEL
					else if ((effects & Defines.EF_GREENGIB) != 0)
					{
						CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
					}
					// RAFAEL
					else if ((effects & Defines.EF_IONRIPPER) != 0)
					{
						CL_fx.IonripperTrail(cent.lerp_origin, ent.origin);
						V.AddLight(ent.origin, 100, 1, 0.5f, 0.5f);
					}
					// RAFAEL
					else if ((effects & Defines.EF_BLUEHYPERBLASTER) != 0)
					{
						V.AddLight(ent.origin, 200, 0, 0, 1);
					}
					// RAFAEL
					else if ((effects & Defines.EF_PLASMA) != 0)
					{
						if ((effects & Defines.EF_ANIM_ALLFAST) != 0)
						{
							CL_fx.BlasterTrail(cent.lerp_origin, ent.origin);
						}
						V.AddLight(ent.origin, 130, 1, 0.5f, 0.5f);
					}
				}
				
				Math3D.VectorCopy(ent.origin, cent.lerp_origin);
			}
		}
		
		// stack variable
		//UPGRADE_NOTE: Final was removed from the declaration of 'gun '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly entity_t gun = new entity_t();
		/*
		* ============== CL_AddViewWeapon ==============
		*/
		internal static void  AddViewWeapon(player_state_t ps, player_state_t ops)
		{
			int i;
			
			// allow the gun to be completely removed
			if (0 == Globals.cl_gun.value_Renamed)
				return ;
			
			// don't draw gun if in wide angle view
			if (ps.fov > 90)
				return ;
			
			//memset( gun, 0, sizeof(gun));
			gun.clear();
			
			if (Globals.gun_model != null)
				gun.model = Globals.gun_model;
			// development tool
			else
				gun.model = Globals.cl.model_draw[ps.gunindex];
			
			if (gun.model == null)
				return ;
			
			// set up gun position
			for (i = 0; i < 3; i++)
			{
				gun.origin[i] = Globals.cl.refdef.vieworg[i] + ops.gunoffset[i] + Globals.cl.lerpfrac * (ps.gunoffset[i] - ops.gunoffset[i]);
				gun.angles[i] = Globals.cl.refdef.viewangles[i] + Math3D.LerpAngle(ops.gunangles[i], ps.gunangles[i], Globals.cl.lerpfrac);
			}
			
			if (Globals.gun_frame != 0)
			{
				gun.frame = Globals.gun_frame; // development tool
				gun.oldframe = Globals.gun_frame; // development tool
			}
			else
			{
				gun.frame = ps.gunframe;
				if (gun.frame == 0)
					gun.oldframe = 0;
				// just changed weapons, don't lerp from old
				else
					gun.oldframe = ops.gunframe;
			}
			
			gun.flags = Defines.RF_MINLIGHT | Defines.RF_DEPTHHACK | Defines.RF_WEAPONMODEL;
			gun.backlerp = 1.0f - Globals.cl.lerpfrac;
			Math3D.VectorCopy(gun.origin, gun.oldorigin); // don't lerp at all
			V.AddEntity(gun);
		}
		
		/*
		* =============== CL_CalcViewValues
		* 
		* Sets cl.refdef view values ===============
		*/
		internal static void  CalcViewValues()
		{
			int i;
			float lerp, backlerp;
			frame_t oldframe;
			player_state_t ps, ops;
			
			// find the previous frame to interpolate from
			ps = Globals.cl.frame.playerstate;
			
			i = (Globals.cl.frame.serverframe - 1) & Defines.UPDATE_MASK;
			oldframe = Globals.cl.frames[i];
			
			if (oldframe.serverframe != Globals.cl.frame.serverframe - 1 || !oldframe.valid)
				oldframe = Globals.cl.frame; // previous frame was dropped or
			// involid
			ops = oldframe.playerstate;
			
			// see if the player entity was teleported this frame
			if (System.Math.Abs(ops.pmove.origin[0] - ps.pmove.origin[0]) > 256 * 8 || System.Math.Abs(ops.pmove.origin[1] - ps.pmove.origin[1]) > 256 * 8 || System.Math.Abs(ops.pmove.origin[2] - ps.pmove.origin[2]) > 256 * 8)
				ops = ps; // don't interpolate
			
			lerp = Globals.cl.lerpfrac;
			
			// calculate the origin
			if ((Globals.cl_predict.value_Renamed != 0) && 0 == (Globals.cl.frame.playerstate.pmove.pm_flags & pmove_t.PMF_NO_PREDICTION))
			{
				// use
				// predicted
				// values
				int delta;
				
				backlerp = 1.0f - lerp;
				for (i = 0; i < 3; i++)
				{
					Globals.cl.refdef.vieworg[i] = Globals.cl.predicted_origin[i] + ops.viewoffset[i] + Globals.cl.lerpfrac * (ps.viewoffset[i] - ops.viewoffset[i]) - backlerp * Globals.cl.prediction_error[i];
				}
				
				// smooth out stair climbing
				delta = (int) (Globals.cls.realtime - Globals.cl.predicted_step_time);
				if (delta < 100)
					Globals.cl.refdef.vieworg[2] = (float) (Globals.cl.refdef.vieworg[2] - Globals.cl.predicted_step * (100 - delta) * 0.01);
			}
			else
			{
				// just use interpolated values
				for (i = 0; i < 3; i++)
					Globals.cl.refdef.vieworg[i] = ops.pmove.origin[i] * 0.125f + ops.viewoffset[i] + lerp * (ps.pmove.origin[i] * 0.125f + ps.viewoffset[i] - (ops.pmove.origin[i] * 0.125f + ops.viewoffset[i]));
			}
			
			// if not running a demo or on a locked frame, add the local angle
			// movement
			if (Globals.cl.frame.playerstate.pmove.pm_type < Defines.PM_DEAD)
			{
				// use
				// predicted
				// values
				for (i = 0; i < 3; i++)
					Globals.cl.refdef.viewangles[i] = Globals.cl.predicted_angles[i];
			}
			else
			{
				// just use interpolated values
				for (i = 0; i < 3; i++)
					Globals.cl.refdef.viewangles[i] = Math3D.LerpAngle(ops.viewangles[i], ps.viewangles[i], lerp);
			}
			
			for (i = 0; i < 3; i++)
				Globals.cl.refdef.viewangles[i] += Math3D.LerpAngle(ops.kick_angles[i], ps.kick_angles[i], lerp);
			
			Math3D.AngleVectors(Globals.cl.refdef.viewangles, Globals.cl.v_forward, Globals.cl.v_right, Globals.cl.v_up);
			
			// interpolate field of view
			Globals.cl.refdef.fov_x = ops.fov + lerp * (ps.fov - ops.fov);
			
			// don't interpolate blend color
			for (i = 0; i < 4; i++)
				Globals.cl.refdef.blend[i] = ps.blend[i];
			
			// add the weapon
			AddViewWeapon(ps, ops);
		}
		
		/*
		* =============== CL_AddEntities
		* 
		* Emits all entities, particles, and lights to the refresh ===============
		*/
		internal static void  AddEntities()
		{
			if (Globals.cls.state != Defines.ca_active)
				return ;
			
			if (Globals.cl.time > Globals.cl.frame.servertime)
			{
				if (Globals.cl_showclamp.value_Renamed != 0)
					Com.Printf("high clamp " + (Globals.cl.time - Globals.cl.frame.servertime) + "\n");
				Globals.cl.time = Globals.cl.frame.servertime;
				Globals.cl.lerpfrac = 1.0f;
			}
			else if (Globals.cl.time < Globals.cl.frame.servertime - 100)
			{
				if (Globals.cl_showclamp.value_Renamed != 0)
					Com.Printf("low clamp " + (Globals.cl.frame.servertime - 100 - Globals.cl.time) + "\n");
				Globals.cl.time = Globals.cl.frame.servertime - 100;
				Globals.cl.lerpfrac = 0;
			}
			else
				Globals.cl.lerpfrac = 1.0f - (Globals.cl.frame.servertime - Globals.cl.time) * 0.01f;
			
			if (Globals.cl_timedemo.value_Renamed != 0)
				Globals.cl.lerpfrac = 1.0f;
			
			/*
			* is ok.. CL_AddPacketEntities (cl.frame); CL_AddTEnts ();
			* CL_AddParticles (); CL_AddDLights (); CL_AddLightStyles ();
			*/
			
			CalcViewValues();
			// PMM - moved this here so the heat beam has the right values for the
			// vieworg, and can lock the beam to the gun
			AddPacketEntities(Globals.cl.frame);
			
			CL_tent.AddTEnts();
			CL_fx.AddParticles();
			CL_fx.AddDLights();
			CL_fx.AddLightStyles();
		}
		
		/*
		* =============== CL_GetEntitySoundOrigin
		* 
		* Called to get the sound spatialization origin ===============
		*/
		public static void  GetEntitySoundOrigin(int ent, float[] org)
		{
			centity_t old;
			
			if (ent < 0 || ent >= Defines.MAX_EDICTS)
				Com.Error(Defines.ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
			old = Globals.cl_entities[ent];
			Math3D.VectorCopy(old.lerp_origin, org);
			
			// FIXME: bmodel issues...
		}
	}
}